package com.gxa.mobile.service.impl;

import com.alibaba.dubbo.config.annotation.Reference;
import com.github.benmanes.caffeine.cache.Cache;
import com.gxa.service.GoodsRpcVo;
import com.gxa.core.result.MessageCode;
import com.gxa.mobile.exception.CustomException;
import com.gxa.mobile.service.GoodsService;
import com.gxa.service.GoodsRpcService;
import com.gxa.mobile.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.List;

/**
 * @author lch
 * @date 2024/3/22 11:39
 * @description 商品实现
 **/
@Slf4j
@Service
public class GoodsServiceImpl implements GoodsService {

    //商品缓存前缀
    public static final String GOODS_CACHE = "goods_cache:";

    //dubbo远程调用
    @Reference
    private GoodsRpcService goodsRpcService;

    //商品缓存对象
    @Autowired
    @Qualifier("goods")
    private Cache<String, GoodsRpcVo> goodsCache;

    //redis
    @Autowired
    private RedisTemplate redisTemplate;

    //redisson分布式锁
    @Autowired
    RedissonClient redisson;


    /**
     * 根据id查询商品详情
     * 1.从本地缓存中获取
     * 2.从redis中获取
     * 3.从数据库查询
     * @param goodsId
     * @return
     */
    @Override
    public GoodsRpcVo findGoodsById(String goodsId) {

        //1.先从本地缓存查询商品,GOODS_CACHE前缀+goodsId作为key去查询商品
        GoodsRpcVo goodsVo = this.goodsCache.getIfPresent(GOODS_CACHE + goodsId);

        if (goodsVo != null) {
            log.info("商品从本地缓存中查询");
            return goodsVo;
        }

        //redis加锁，防止缓存雪崩
        RLock lock=redisson.getLock(goodsId);
        try {
            lock.lock();
            //2.从redis中查询商品
            Object result = redisTemplate.boundValueOps(GOODS_CACHE + goodsId).get();
            log.info("商品从redis中查询");

            if (result != null && !StringUtils.isEmpty(result.toString())) {

                //判断是否是不存在的商品
                if(result.equals("noGoods")){
                    log.info("不存在的商品goodsId:{}",goodsId);
                    throw new CustomException(MessageCode.NO_GOODS);
                }


                //redis中json格式转换成商品对象
                goodsVo = JsonUtils.jsonToPojo(result.toString(), GoodsRpcVo.class);

                //把商品放入本地缓存
                goodsCache.put(GOODS_CACHE + goodsId, goodsVo);
                return goodsVo;
            }

            //3.从mysql中查询，发起Rpc远程调用
            goodsVo = this.goodsRpcService.findGoodsById(Integer.valueOf(goodsId));
            log.info("商品从mysql中查询");

            //mysql没有查询到，不存在该商品
            if(goodsVo==null){
                //把不存在的商品保存到redis中，防止缓存穿透
                redisTemplate.boundValueOps(GOODS_CACHE+goodsId).set("noGoods");
                throw new CustomException(MessageCode.NO_GOODS);
            }

            //mysql查询到商品，放入redis中
            redisTemplate.boundValueOps(GOODS_CACHE + goodsId).set(JsonUtils.objectToJson(goodsVo));
            return goodsVo;
        } finally {
            //释放锁
            if (lock.isLocked()) {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
    }


    /**
     * 查询所有商品
     * @return
     */
    @Override
    public List<GoodsRpcVo> findAllGoods() {

        //发起Rpc远程调用
        return this.goodsRpcService.findAllGoods();
    }


    /**
     * 删除商品redis缓存
     * @param goodsId
     */
    @Override
    public void deleteGoodsCache(Integer goodsId) {

        redisTemplate.delete(GOODS_CACHE+goodsId);
        log.info("删除商品缓存:{}",goodsId);
    }
}
